package it.sandler.android.database;

import it.sandler.android.database.types.SandlerBool;
import it.sandler.android.database.types.SandlerFloat;
import it.sandler.android.database.types.SandlerInteger;
import it.sandler.android.database.types.SandlerLong;
import it.sandler.android.database.types.SandlerFieldRelation;
import it.sandler.android.database.types.SandlerText;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;

/**
 * You can do what ever you want to do with SandlerDB.<br>
 * There are no restrictions. You can use SandlerDB for commercial use.<br>
 * <br>
 * Circle me on Google+: http://gplus.to/roberthahn<br>
 * SandlerDB on Google Code: http://code.google.com/p/sandler-db-for-android/<br>
 * <br>
 * Date: 04/16/2012
 * 
 * @author Robert Hahn
 * 
 */
public class SandlerField {

	private final Field field;
	private SandlerRelationTable relation;

	protected SandlerField(Field field) {
		this.field = field;

		if (hasRelation()) {
			Class<?> firstClass = field.getDeclaringClass();
			Class<?> secondClass = getGenericType(field);
			setRelation(new SandlerRelationTable(firstClass, secondClass));
		}
	}

	/**
	 * @return null if NO field should be created for this {@link SandlerField}
	 */
	protected static String getFieldName(Field field) {
		if (isSandlerType(field))
			return field.getName();
		if (isSandlerEntity(field))
			return field.getName() + "___rel___"
					+ getGenericType(field).getSimpleName();
		// its a list of sth
		return null;
	}

	protected String getFieldName() {
		return getFieldName(field);
	}

	/**
	 * @return null if NO field should be created for this {@link SandlerField}
	 */
	protected String getFieldType() {
		if (isSandlerType(field))
			// null if its not bool, float, number or text
			return getDatabaseType();
		if (isSandlerEntity(field))
			// for holding the id to the reference object
			return "INTEGER(64)";
		// a list has its own relation table
		return null;
	}

	private String getDatabaseType() {
		Class<?> c = field.getType();
		if (c.equals(SandlerBool.class))
			return "BOOL";
		if (c.equals(SandlerFloat.class))
			return "FLOAT";
		if (c.equals(SandlerInteger.class))
			return "INTEGER";
		if (c.equals(SandlerText.class))
			return "TEXT";
		if (c.equals(SandlerLong.class))
			return "INTEGER(64)";
		return null;
	}

	/**
	 * @return true if a {@link SandlerRelationTable} needs to be created for
	 *         this field
	 */
	protected boolean hasRelation() {
		return isList(getField());
	}

	/**
	 * @return true if the given Field's type can be used as a table field
	 */
	protected static boolean isSandler(Field f) {
		return isSandlerEntity(f) || isList(f) || isSandlerType(f);
	}

	/**
	 * @return true if the given Field's type is SandlerField
	 */
	protected static boolean isSandlerType(Field f) {
		Class<?> superclass = f.getType().getSuperclass();
		return !f.getType().getSimpleName()
				.equals(SandlerFieldRelation.class.getSimpleName())
				&& superclass != null
				&& superclass.getSimpleName().equals(
						SandlerType.class.getSimpleName());
	}

	/**
	 * @return true if the given Field's type extends SandlerField
	 */
	protected static boolean isSandlerEntity(Field f) {
		return f.getType().getSimpleName()
				.equals(SandlerFieldRelation.class.getSimpleName());
	}

	/**
	 * @return true if the given Field's type is a list and extends a
	 *         SandlerEntity OR SandlerField
	 */
	protected static boolean isList(Field f) {
		if (!f.getType().getSimpleName()
				.equals(SandlerList.class.getSimpleName()))
			return false;
		Class<?> genericType = getGenericType(f); // SandlerList<T>
		Class<?> sClass = genericType.getSuperclass(); // SandlerEntity
		return sClass != null
				&& (sClass.getSimpleName().equals(SandlerEntity.class
						.getSimpleName()));
	}

	/**
	 * @return true if the given Field's type is a list and extends a
	 *         SandlerEntity OR SandlerField
	 */
	protected static Class<?> getGenericType(Field f) {
		ParameterizedType listType = (ParameterizedType) f.getGenericType();
		Class<?> genericListClass = (Class<?>) listType
				.getActualTypeArguments()[0];
		return genericListClass;
	}

	protected Field getField() {
		return field;
	}

	protected SandlerRelationTable getRelation() {
		return relation;
	}

	private void setRelation(SandlerRelationTable relation) {
		this.relation = relation;
	}
}
